﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace SCG.General
{
    //A class that makes it easier to tokenize/de-tokenize strings
    //Arguments are tokenized with length information, so delimiter is not required
    //Derived classes can override virtual methods to tokenize/detokenize different data types
    public class Tokenizer
    {
        //create an empty tokenizer - for tokenizing purposes
        public Tokenizer()
        {
        }

        //create an instance of a Tokenizer from the specified string
        //use TokenXXX methods below to extract tokens one by one
        public Tokenizer(string source)
        {
            while (source.Length > 0)
            {
                int pos = source.IndexOf('|');
                int length = Int32.Parse(source.Substring(0, pos));
                _tokens.Add(source.Substring(pos + 1, length));
                source = source.Substring(pos + length + 1);
            }
        }

        //tokenize a group of objects into a string
        public string Tokenize(params object[] tokens)
        {       
            StringBuilder sb = new StringBuilder();
            foreach (object obj in tokens)
            {
                string token = TokenizeObject(obj);
                sb.Append(token.Length.ToString());
                sb.Append("|");
                sb.Append(token);              
            }
            _result += sb.ToString();   
            return Result;
        }

        //Append a new token
        public string AppendToken(object obj)
        {
            string token = TokenizeObject(obj);
            _result += token.Length + "|" + token;
            return Result;
        }

        //Access the result
        public string Result
        {
            get
            {
                return _result;
            }
        }

        //Determine if the tokenizer has any more tokens
        public bool HasTokens
        {
            get
            {
                return _tokens.Count > _n;
            }
        }

        //Get the next token as a specific data type
        public string TokenString
        {
            get
            {
                return _tokens[_n++];
            }
        }
        public int TokenInt
        {
            get
            {
                return Int32.Parse(_tokens[_n++]);
            }
        }
        public double TokenDouble
        {
            get
            {
                return Double.Parse(_tokens[_n++], CULTURE);
            }
        }
        public DateTime TokenDateTime
        {
            get
            {
                return new DateTime(Int64.Parse(_tokens[_n++]));
            }
        }
        public bool TokenBoolean
        {
            get
            {
                return Boolean.Parse(_tokens[_n++]);
            }
        }
        public byte TokenByte
        {
            get
            {
                return Byte.Parse(_tokens[_n++]);
            }
        }
        public SCGColor TokenColor
        {
            get
            {
                return StringToColor(TokenString);
            }
        }
        public char TokenChar
        {
            get
            {
                return TokenString[0];
            }
        }

        //Color to string conversion helpers
        public static string ColorToString(SCGColor c)
        {
            return c.A + "|" + c.R + "|" + c.G + "|" + c.B;
        }
        public static SCGColor StringToColor(string s)
        {
            string[] argb = s.Split('|');
            byte a = Byte.Parse(argb[0]);
            byte r = Byte.Parse(argb[1]);
            byte g = Byte.Parse(argb[2]);
            byte b = Byte.Parse(argb[3]);
            return new SCGColor(a, r, g, b);
        }

        //private members
        public static CultureInfo CULTURE = new CultureInfo("en-US");
        private List<string> _tokens = new List<string>();
        int _n = 0;
        private string _result = "";

        //Tokenize an object
        //Derived classes should override to provide type specific tokenizing
        protected virtual string TokenizeObject(object obj)
        {
            if (obj is double)
            {
                double x = (double)obj;
                return x.ToString(CULTURE);
            }
            else if (obj is DateTime)
            {
                DateTime dt = (DateTime)obj;
                return dt.Ticks.ToString();
            }
            else if (obj is SCGColor)
            {
                SCGColor c = (SCGColor)obj;
                return ColorToString(c);
            }
            else if (obj is char)
            {
                char c = (char)obj;
                return "" + c;
            }
            else
                return obj.ToString();
        }

        //Allow derived classes to get the next string token and advance the token counter
        protected string NextToken
        {
            get
            {
                return _tokens[_n++];
            }
        }
    }
}
